#include <pthread.h>
#include <string>
#include <iostream>
#include <unistd.h>

int ticket_number = 1000;

class ThreadData
{
public:
    ThreadData(const std::string &name, pthread_mutex_t &lock)
        : _name(name), _lockp(&lock)
    {
    }

    ~ThreadData()
    {
    }

    std::string _name;
    pthread_mutex_t *_lockp;
};

void *thread_function(void *args)
{
    ThreadData *td = static_cast<ThreadData *>(args);
    while (1)
    {
        // 对临界资源的保护本质上就是用锁，对临界区的代码进行保护
        // 加锁之后，允许线程进行切换，因为锁仍然被其他某个进程持有，所以即使持有
        // 锁的进程被切换走，其他线程也要等持有锁的线程把锁释放，再竞争锁，才能获得访问
        // 临界区的资格
        // 在我执行期间，其他线程不会来打扰我，这也是原子性的一种体现

        // 锁的实现可以分为两种，一种是硬件级，一种是软件级
        // 硬件级方法就是直接短暂关闭时钟中断即可，但是这样如果处理不好可能会导致严重的系统错误
        // 软件级方法就是通过swap/exchange这样的指令将内存中锁的内容和寄存器al进行互换，
        // 来判断当前线程是否获得了锁（比如说mutex在内存中是1,al寄存器原先内容是0,这时
        // 将两者内容互换，当掐线程就获得了锁，其他进程再进行此操作不能让al寄存器获得1
        // 也就进入阻塞等待状态，直到当前线程的al寄存器内容为1）

        // 理解锁的前提：cpu内的寄存器硬件只有一套，但是cpu寄存器可以有很多份，各自一份
        // 当前执行流的上下文；换句话说，把一个变量的内容交换到cpu寄存器内部，就是将
        // 该变量的内容放入当前执行流的硬件上下文当中；当前cpu寄存器的硬件 上下文属于
        // 进程或者线程私有的；我们用swap或者exchange将内存中的变量交换到cpu的寄存器当中
        // 是交换，不是拷贝，所以谁申请到，谁就获得锁

        // 高频的获得锁而不执行有效动作，会导致其他线程线程饥饿
        //让所有的执行流访问临界资源后，如仍需访问共享资源那就要重新排队，按照一定的顺序
        //访问资源，这就是线程同步
        {
            pthread_mutex_lock(td->_lockp);
            if (ticket_number > 0)
            {
                usleep(1000);
                ticket_number--;
                std::cout << td->_name << "抢到了票号" << ticket_number << std::endl;
                pthread_mutex_unlock(td->_lockp);
            }
            else
            {
                pthread_mutex_unlock(td->_lockp);
                break;
            }
        }
    }
    return nullptr;
}

int main()
{
    pthread_mutex_t lock;
    pthread_mutex_init(&lock, nullptr); // 初始化锁

    pthread_t t1;
    pthread_t t2;
    pthread_t t3;
    pthread_t t4;

    // 利用结构体来获得锁和线程名称
    ThreadData *td1 = new ThreadData("thread-1", lock);
    ThreadData *td2 = new ThreadData("thread-2", lock);
    ThreadData *td3 = new ThreadData("thread-3", lock);
    ThreadData *td4 = new ThreadData("thread-4", lock);

    pthread_create(&t1, nullptr, thread_function, (void *)td1);
    pthread_create(&t2, nullptr, thread_function, (void *)td2);
    pthread_create(&t3, nullptr, thread_function, (void *)td3);
    pthread_create(&t4, nullptr, thread_function, (void *)td4);

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);
    pthread_join(t4, nullptr);

    pthread_mutex_destroy(&lock); // 设置的非全局锁需要手动进行释放，但是全局的锁不需要手动释放

    return 0;
}